/*-------------------------------------------------------------------------
 *
 * encode.c
 *	  各种数据编码/解码的内容。
 *
 * Copyright (c) 2001-2022, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/encode.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <ctype.h>

#include "mb/pg_wchar.h"
#include "utils/builtins.h"
#include "utils/memutils.h"


/*
 * 编码转换 API。
 * encode_len() 和 decode_len() 计算所需空间的大小，
 * 而 encode() 和 decode() 执行实际转换。
 * _len 函数返回一个大于所需空间的数值是可以的，但不能低于所需。
 * （说到这里，过大的高估可能会导致不必要的错误，
 * 所以最好保证准确。）转换例程将写入 *res 的缓冲区并返回其输出的实际长度。
 */
struct pg_encoding
{
	uint64		(*encode_len) (const char *data, size_t dlen);
	uint64		(*decode_len) (const char *data, size_t dlen);
	uint64		(*encode) (const char *data, size_t dlen, char *res);
	uint64		(*decode) (const char *data, size_t dlen, char *res);
};

static const struct pg_encoding *pg_find_encoding(const char *fc_name);

/*
 * SQL 函数。
 */

Datum binary_encode(PG_FUNCTION_ARGS)
{
	bytea	   *fc_data = PG_GETARG_BYTEA_PP(0);
	Datum		fc_name = PG_GETARG_DATUM(1);
	text	   *fc_result;
	char	   *fc_namebuf;
	char	   *fc_dataptr;
	size_t		fc_datalen;
	uint64		fc_resultlen;
	uint64		fc_res;
	const struct pg_encoding *fc_enc;

	fc_namebuf = TextDatumGetCString(fc_name);

	fc_enc = pg_find_encoding(fc_namebuf);
	if (fc_enc == NULL)
#ifdef FDD //cppcheck
	{
#endif
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("unrecognized encoding: \"%s\"", fc_namebuf)));
#ifdef FDD //cppcheck
		PG_RETURN_VOID();
	}
#endif
	fc_dataptr = VARDATA_ANY(fc_data);
	fc_datalen = VARSIZE_ANY_EXHDR(fc_data);

	fc_resultlen = fc_enc->encode_len(fc_dataptr, fc_datalen);

	/*
	 * resultlen 可能会溢出 uint32，因此在 32 位机器上依赖 palloc 的内部检查是不安全的。
	 */
	if (fc_resultlen > MaxAllocSize - VARHDRSZ)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("result of encoding conversion is too large")));

	fc_result = palloc(VARHDRSZ + fc_resultlen);

	fc_res = fc_enc->encode(fc_dataptr, fc_datalen, VARDATA(fc_result));

	/* 将其设为 FATAL，因为我们碰到了内存... */
	if (fc_res > fc_resultlen)
		elog(FATAL, "overflow - encode estimate too small");

	SET_VARSIZE(fc_result, VARHDRSZ + fc_res);

	PG_RETURN_TEXT_P(fc_result);
}

Datum binary_decode(PG_FUNCTION_ARGS)
{
	text	   *fc_data = PG_GETARG_TEXT_PP(0);
	Datum		fc_name = PG_GETARG_DATUM(1);
	bytea	   *fc_result;
	char	   *fc_namebuf;
	char	   *fc_dataptr;
	size_t		fc_datalen;
	uint64		fc_resultlen;
	uint64		fc_res;
	const struct pg_encoding *fc_enc;

	fc_namebuf = TextDatumGetCString(fc_name);

	fc_enc = pg_find_encoding(fc_namebuf);
	if (fc_enc == NULL)
#ifdef FDD //cppcheck
	{
#endif
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("unrecognized encoding: \"%s\"", fc_namebuf)));
#ifdef FDD //cppcheck
		PG_RETURN_VOID();
	}
#endif
	fc_dataptr = VARDATA_ANY(fc_data);
	fc_datalen = VARSIZE_ANY_EXHDR(fc_data);

	fc_resultlen = fc_enc->decode_len(fc_dataptr, fc_datalen);

	/*
	 * resultlen 可能会溢出 uint32，因此在 32 位机器上依赖 palloc 的内部检查是不安全的。
	 */
	if (fc_resultlen > MaxAllocSize - VARHDRSZ)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("result of decoding conversion is too large")));

	fc_result = palloc(VARHDRSZ + fc_resultlen);

	fc_res = fc_enc->decode(fc_dataptr, fc_datalen, VARDATA(fc_result));

	/* 将其设为 FATAL，因为我们碰到了内存... */
	if (fc_res > fc_resultlen)
		elog(FATAL, "overflow - decode estimate too small");

	SET_VARSIZE(fc_result, VARHDRSZ + fc_res);

	PG_RETURN_BYTEA_P(fc_result);
}


/*
 * HEX
 */

static const char hextbl[] = "0123456789abcdef";

static const int8 hexlookup[128] = {
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
	-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
};

uint64 hex_encode(const char *fc_src, size_t fc_len, char *fc_dst)
{
	const char *fc_end = fc_src + fc_len;

	while (fc_src < fc_end)
	{
		*fc_dst++ = hextbl[(*fc_src >> 4) & 0xF];
		*fc_dst++ = hextbl[*fc_src & 0xF];
		fc_src++;
	}
	return (uint64) fc_len * 2;
}

static inline char get_hex(const char *fc_cp)
{
	unsigned char fc_c = (unsigned char) *fc_cp;
	int			fc_res = -1;

	if (fc_c < 127)
		fc_res = hexlookup[fc_c];

	if (fc_res < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid hexadecimal digit: \"%.*s\"",
						pg_mblen(fc_cp), fc_cp)));

	return (char) fc_res;
}

uint64 hex_decode(const char *fc_src, size_t fc_len, char *fc_dst)
{
	const char *fc_s,
			   *fc_srcend;
	char		fc_v1,
				fc_v2,
			   *fc_p;

	fc_srcend = fc_src + fc_len;
	fc_s = fc_src;
	fc_p = fc_dst;
	while (fc_s < fc_srcend)
	{
		if (*fc_s == ' ' || *fc_s == '\n' || *fc_s == '\t' || *fc_s == '\r')
		{
			fc_s++;
			continue;
		}
		fc_v1 = get_hex(fc_s) << 4;
		fc_s++;
		if (fc_s >= fc_srcend)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("invalid hexadecimal data: odd number of digits")));

		fc_v2 = get_hex(fc_s);
		fc_s++;
		*fc_p++ = fc_v1 | fc_v2;
	}

	return fc_p - fc_dst;
}

static uint64 fc_hex_enc_len(const char *fc_src, size_t fc_srclen)
{
	return (uint64) fc_srclen << 1;
}

static uint64 fc_hex_dec_len(const char *fc_src, size_t fc_srclen)
{
	return (uint64) fc_srclen >> 1;
}

/*
 * BASE64
 */

static const char _base64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static const int8 b64lookup[128] = {
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
	52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
	-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
	15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
	-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
	41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
};

static uint64 pg_base64_encode(const char *fc_src, size_t fc_len, char *fc_dst)
{
	char	   *fc_p,
			   *fc_lend = fc_dst + 76;
	const char *fc_s,
			   *fc_end = fc_src + fc_len;
	int			fc_pos = 2;
	uint32		fc_buf = 0;

	fc_s = fc_src;
	fc_p = fc_dst;

	while (fc_s < fc_end)
	{
		fc_buf |= (unsigned char) *fc_s << (fc_pos << 3);
		fc_pos--;
		fc_s++;

		/* 写出它 */
		if (fc_pos < 0)
		{
			*fc_p++ = _base64[(fc_buf >> 18) & 0x3f];
			*fc_p++ = _base64[(fc_buf >> 12) & 0x3f];
			*fc_p++ = _base64[(fc_buf >> 6) & 0x3f];
			*fc_p++ = _base64[fc_buf & 0x3f];

			fc_pos = 2;
			fc_buf = 0;
		}
		if (fc_p >= fc_lend)
		{
			*fc_p++ = '\n';
			fc_lend = fc_p + 76;
		}
	}
	if (fc_pos != 2)
	{
		*fc_p++ = _base64[(fc_buf >> 18) & 0x3f];
		*fc_p++ = _base64[(fc_buf >> 12) & 0x3f];
		*fc_p++ = (fc_pos == 0) ? _base64[(fc_buf >> 6) & 0x3f] : '=';
		*fc_p++ = '=';
	}

	return fc_p - fc_dst;
}

static uint64 pg_base64_decode(const char *fc_src, size_t fc_len, char *fc_dst)
{
	const char *fc_srcend = fc_src + fc_len,
			   *fc_s = fc_src;
	char	   *fc_p = fc_dst;
	char		fc_c;
	int			fc_b = 0;
	uint32		fc_buf = 0;
	int			fc_pos = 0,
				fc_end = 0;

	while (fc_s < fc_srcend)
	{
		fc_c = *fc_s++;

		if (fc_c == ' ' || fc_c == '\t' || fc_c == '\n' || fc_c == '\r')
			continue;

		if (fc_c == '=')
		{
			/* 结束序列 */
			if (!fc_end)
			{
				if (fc_pos == 2)
					fc_end = 1;
				else if (fc_pos == 3)
					fc_end = 2;
				else
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
							 errmsg("unexpected \"=\" while decoding base64 sequence")));
			}
			fc_b = 0;
		}
		else
		{
			fc_b = -1;
			if (fc_c > 0 && fc_c < 127)
				fc_b = b64lookup[(unsigned char) fc_c];
			if (fc_b < 0)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
						 errmsg("invalid symbol \"%.*s\" found while decoding base64 sequence",
								pg_mblen(fc_s - 1), fc_s - 1)));
		}
		/* 将其添加到缓冲区 */
		fc_buf = (fc_buf << 6) + fc_b;
		fc_pos++;
		if (fc_pos == 4)
		{
			*fc_p++ = (fc_buf >> 16) & 255;
			if (fc_end == 0 || fc_end > 1)
				*fc_p++ = (fc_buf >> 8) & 255;
			if (fc_end == 0 || fc_end > 2)
				*fc_p++ = fc_buf & 255;
			fc_buf = 0;
			fc_pos = 0;
		}
	}

	if (fc_pos != 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid base64 end sequence"),
				 errhint("Input data is missing padding, is truncated, or is otherwise corrupted.")));

	return fc_p - fc_dst;
}


static uint64 pg_base64_enc_len(const char *fc_src, size_t fc_srclen)
{
	/* 3 个字节将被转换为 4，换行符在 76 个字符后 */
	return ((uint64) fc_srclen + 2) * 4 / 3 + (uint64) fc_srclen / (76 * 3 / 4);
}

static uint64 pg_base64_dec_len(const char *fc_src, size_t fc_srclen)
{
	return ((uint64) fc_srclen * 3) >> 2;
}

/*
 * 转义
 * 最小限度地将 bytea 转义为文本。
 * 将文本去除转义为 bytea。
 *
 * 我们必须转义零字节和高位设置字节，以避免生成
 * 在当前编码中可能无效的文本，或者在通过编码转换
 * 时可能会更改为其他内容（导致无法还原为原始 bytea 值）。
 * 当然，反斜杠本身也必须被转义。
 *
 * 去转义的过程是 \\ 和任何 \### 八进制。
 */

#define VAL(CH)			((CH) - '0')
#define DIG(VAL)		((VAL) + '0')

static uint64 fc_esc_encode(const char *fc_src, size_t fc_srclen, char *fc_dst)
{
	const char *fc_end = fc_src + fc_srclen;
	char	   *fc_rp = fc_dst;
	uint64		fc_len = 0;

	while (fc_src < fc_end)
	{
		unsigned char fc_c = (unsigned char) *fc_src;

		if (fc_c == '\0' || IS_HIGHBIT_SET(fc_c))
		{
			fc_rp[0] = '\\';
			fc_rp[1] = DIG(fc_c >> 6);
			fc_rp[2] = DIG((fc_c >> 3) & 7);
			fc_rp[3] = DIG(fc_c & 7);
			fc_rp += 4;
			fc_len += 4;
		}
		else if (fc_c == '\\')
		{
			fc_rp[0] = '\\';
			fc_rp[1] = '\\';
			fc_rp += 2;
			fc_len += 2;
		}
		else
		{
			*fc_rp++ = fc_c;
			fc_len++;
		}

		fc_src++;
	}

	return fc_len;
}

static uint64 fc_esc_decode(const char *fc_src, size_t fc_srclen, char *fc_dst)
{
	const char *fc_end = fc_src + fc_srclen;
	char	   *fc_rp = fc_dst;
	uint64		fc_len = 0;

	while (fc_src < fc_end)
	{
		if (fc_src[0] != '\\')
			*fc_rp++ = *fc_src++;
		else if (fc_src + 3 < fc_end &&
				 (fc_src[1] >= '0' && fc_src[1] <= '3') &&
				 (fc_src[2] >= '0' && fc_src[2] <= '7') &&
				 (fc_src[3] >= '0' && fc_src[3] <= '7'))
		{
			int			fc_val;

			fc_val = VAL(fc_src[1]);
			fc_val <<= 3;
			fc_val += VAL(fc_src[2]);
			fc_val <<= 3;
			*fc_rp++ = fc_val + VAL(fc_src[3]);
			fc_src += 4;
		}
		else if (fc_src + 1 < fc_end &&
				 (fc_src[1] == '\\'))
		{
			*fc_rp++ = '\\';
			fc_src += 2;
		}
		else
		{
			/*
			 * 一个反斜杠，不后跟 ### 有效八进制。
			 * 不应该到这里，因为 esc_dec_len 做了相同的检查。
			 */
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("invalid input syntax for type %s", "bytea")));
		}

		fc_len++;
	}

	return fc_len;
}

static uint64 fc_esc_enc_len(const char *fc_src, size_t fc_srclen)
{
	const char *fc_end = fc_src + fc_srclen;
	uint64		fc_len = 0;

	while (fc_src < fc_end)
	{
		if (*fc_src == '\0' || IS_HIGHBIT_SET(*fc_src))
			fc_len += 4;
		else if (*fc_src == '\\')
			fc_len += 2;
		else
			fc_len++;

		fc_src++;
	}

	return fc_len;
}

static uint64 fc_esc_dec_len(const char *fc_src, size_t fc_srclen)
{
	const char *fc_end = fc_src + fc_srclen;
	uint64		fc_len = 0;

	while (fc_src < fc_end)
	{
		if (fc_src[0] != '\\')
			fc_src++;
		else if (fc_src + 3 < fc_end &&
				 (fc_src[1] >= '0' && fc_src[1] <= '3') &&
				 (fc_src[2] >= '0' && fc_src[2] <= '7') &&
				 (fc_src[3] >= '0' && fc_src[3] <= '7'))
		{
			/*
			 * 反斜杠 + 有效八进制
			 */
			fc_src += 4;
		}
		else if (fc_src + 1 < fc_end &&
				 (fc_src[1] == '\\'))
		{
			/*
			 * 两个反斜杠 = 反斜杠
			 */
			fc_src += 2;
		}
		else
		{
			/*
			 * 一个反斜杠，不后跟 ### 有效八进制
			 */
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("invalid input syntax for type %s", "bytea")));
		}

		fc_len++;
	}
	return fc_len;
}

/*
 * 通用
 */

static const struct
{
	const char *name;
	struct pg_encoding enc;
}			enclist[] =

{
	{
		"hex",
		{
			fc_hex_enc_len, fc_hex_dec_len, hex_encode, hex_decode
		}
	},
	{
		"base64",
		{
			pg_base64_enc_len, pg_base64_dec_len, pg_base64_encode, pg_base64_decode
		}
	},
	{
		"escape",
		{
			fc_esc_enc_len, fc_esc_dec_len, fc_esc_encode, fc_esc_decode
		}
	},
	{
		NULL,
		{
			NULL, NULL, NULL, NULL
		}
	}
};

static const struct pg_encoding *
pg_find_encoding(const char *fc_name)
{
	int			fc_i;

	for (fc_i = 0; enclist[fc_i].name; fc_i++)
		if (pg_strcasecmp(enclist[fc_i].name, fc_name) == 0)
			return &enclist[fc_i].enc;

	return NULL;
}
